package com.driver.core;


import com.driver.core.mapper.generator.MapperGenerator;
import com.driver.core.mapper.generator.SqlXMLGenerator;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class FileManager {
    protected static final String fileEntity = DBManager.getConfig().getEntityPackageName();
    protected static final String fileMapper = DBManager.getConfig().getMapperPackageName();
    protected static final boolean isLombok = DBManager.getConfig().isLombok();
    protected static final String fileService = DBManager.getConfig().getServicePackageName();
    protected static final String fileController = DBManager.getConfig().getControllerPackageName();
    protected static final String packgaePreffix = DBManager.getConfig().getPackagePreffix().replaceAll("\\.", "\\\\");
    private static final String rootPath = System.getProperty("user.dir") + "\\src\\" + packgaePreffix + "\\";

    public static void createFile(Table table) {
        createJavaFile(table);
        createMapperFile(table);
        createXMLFile(table);
        createIServiceFile(table);
        createServiceImplFile(table);
    }

    private static void createServiceImplFile(Table table) {
        String privateKey="id";
        String type="Integer";
        for (Column column : table.getColumns()) {
            if (column.isPrivateKey()) {
                privateKey = column.getPropertyName();
                type = column.getPropertyType();
            }
        }


        StringBuilder sb = new StringBuilder();
        sb.append("package ").append(DBManager.getConfig().getPackagePreffix()).append(".").append(fileService).append(".impl").append(";\n\n");

        sb.append("import ").append(DBManager.getConfig().getPackagePreffix()).append(".").append(fileService).append(".").append("I").append(table.getClassName()).append("Service;\n");
        sb.append("import ").append(DBManager.getConfig().getPackagePreffix()).append(".").append(fileMapper).append(".").append(table.getClassName()).append("Mapper;\n");
        sb.append("import org.springframework.stereotype.Service;\n");
        sb.append("import org.springframework.stereotype.Service;\n");
        sb.append("import lombok.extern.slf4j.Slf4j;\n");
        sb.append("import org.springframework.transaction.annotation.Transactional;\n");
        sb.append("import ").append(DBManager.getConfig().getPackagePreffix()).append(".").append(fileEntity).append(".").append(table.getClassName()).append(";").append("\n");
        sb.append("import java.util.List;\n");
        sb.append("import java.util.Map;\n");
        sb.append("import javax.annotation.Resource;\n\n");
        sb.append("@Service\n");
        sb.append("@Slf4j\n");
        sb.append("public class ").append(table.getClassName()).append("ServiceImpl implements I").append(table.getClassName()).append("Service {\n");
        sb.append("    @Resource\n");
        sb.append("    private ").append(table.getClassName()).append("Mapper ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper;");
        sb.append("\n\n");

        sb.append("    @Override\n");
        sb.append("    public ").append(table.getClassName()).append(" getOne").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition").append(")").append(" {\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.selectOne(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition);\n    }\n\n");
        sb.append("    @Override\n");
        sb.append("    public ").append("List<").append(table.getClassName()).append(">").append(" getList").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition").append(")").append(" {\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.selectByList(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition);\n    }\n\n");
        sb.append("    @Override\n");
        sb.append("    public ").append("List<Map<String, Object>>").append(" getMaps").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition").append(")").append(" {\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.selectByMaps(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition);\n    }\n\n");


        sb.append("    @Override\n");
        sb.append("    public ").append(table.getClassName()).append(" getOne").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition,").append(" String columnName").append(")").append(" {\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.selectColumnNameByOne(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition, columnName);\n    }\n\n");
        sb.append("    @Override\n");
        sb.append("    public ").append("List<").append(table.getClassName()).append(">").append(" getList").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition,").append(" String columnName").append(")").append(" {\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.selectColumnNameByList(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition, columnName);\n    }\n\n");
        sb.append("    @Override\n");
        sb.append("    public ").append("List<Map<String, Object>>").append(" getMaps").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition,").append(" String columnName").append(")").append(" {\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.selectColumnNameByMaps(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition, columnName);\n    }\n\n");
        sb.append("    @Override\n");
        sb.append("    public ").append("List<Map<String, Object>>").append(" selectBySql").append("(").append("String ").append(" ").append("sql").append(") {\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.selectBySql(").append("sql);\n    }\n\n");

        sb.append("    @Override\n");
        sb.append("    @Transactional\n");
        sb.append("    public ").append("int").append(" insert").append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append(")").append(" {\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.insert(").append(StringUtil.lowerFirstName(table.getClassName())).append(");\n    }\n\n");
        sb.append("    @Override\n");
        sb.append("    @Transactional\n");
        sb.append("    public ").append("int").append(" update").append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition, ").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append(")").append(" {\n").append("        ToolUtil.checkWhereSql(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition);\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.update(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition, ").append(StringUtil.lowerFirstName(table.getClassName())).append(");\n    }\n\n");


        sb.append("    @Override\n");
        sb.append("    @Transactional\n");
        sb.append("    public ").append("int").append(" deleteBy").append(StringUtil.upperFirstName(privateKey)).append("(").append(type).append(" ").append(privateKey).append(")").append(" {\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.deleteBy").append(StringUtil.upperFirstName(privateKey)).append("(").append(privateKey).append(");\n    }\n\n");

        sb.append("    @Override\n");
        sb.append("    @Transactional\n");
        sb.append("    public ").append("int").append(" delete").append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition)").append(" {\n").append("        ToolUtil.checkWhereSql(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition);\n").append("        return ").append(StringUtil.lowerFirstName(table.getClassName())).append("Mapper.delete(").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition").append(");\n    }\n\n");


        sb.append("\n}");
        createFile(table.getClassName() + "ServiceImpl.java", rootPath + fileService + "\\impl\\", sb.toString());
    }

    private static void createIServiceFile(Table table) {
        String privateKey="id";
        String type="Integer";
        for (Column column : table.getColumns()) {
            if (column.isPrivateKey()) {
                privateKey = column.getPropertyName();
                type = column.getPropertyType();
            }
        }


        StringBuilder sb = new StringBuilder();
        sb.append("package ").append(DBManager.getConfig().getPackagePreffix()).append(".").append(fileService).append(";\n\n");
        sb.append("import ").append(DBManager.getConfig().getPackagePreffix()).append(".").append(fileEntity).append(".").append(table.getClassName()).append(";").append("\n");
        sb.append("import java.util.List;\n");
        sb.append("import java.util.Map;\n\n");
        sb.append("public interface I").append(table.getClassName()).append("Service {\n\n");

        sb.append("    ").append(table.getClassName()).append(" getOne").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition").append(");\n\n");
        sb.append("    ").append("List<").append(table.getClassName()).append(">").append(" getList").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition").append(");\n\n");
        sb.append("    ").append("List<Map<String, Object>>").append(" getMaps").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition").append(");\n\n");


        sb.append("    ").append(table.getClassName()).append(" getOne").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition,").append(" String columnName").append(");\n\n");
        sb.append("    ").append("List<").append(table.getClassName()).append(">").append(" getList").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition,").append(" String columnName").append(");\n\n");
        sb.append("    ").append("List<Map<String, Object>>").append(" getMaps").append(table.getClassName()).append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition,").append(" String columnName").append(");\n\n");
        sb.append("    ").append("List<Map<String, Object>>").append(" selectBySql").append("(").append("String ").append(" ").append("sql").append(");\n\n");

        sb.append("    ").append("int").append(" insert").append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append(");\n\n");
        sb.append("    ").append("int").append(" update").append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition, ").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append(");\n\n");
//        sb.append("    ").append("int").append(" updateBy").append(StringUtil.upperFirstName(privateKey)).append("(").append(type).append(" ").append(privateKey).append(");\n\n");

        sb.append("    ").append("int").append(" deleteBy").append(StringUtil.upperFirstName(privateKey)).append("(").append(type).append(" ").append(privateKey).append(");\n\n");

        sb.append("    ").append("int").append(" delete").append("(").append(table.getClassName()).append(" ").append(StringUtil.lowerFirstName(table.getClassName())).append("Condition").append(");\n\n");


        sb.append("\n}");
        createFile("I" + table.getClassName() + "Service.java", rootPath + fileService + "\\", sb.toString());
    }


    private static void createXMLFile(Table table) {
        StringBuilder sb = new StringBuilder();
        ReflectUtil.getMapperMethod(sb, SqlXMLGenerator.class, new SqlXMLGenerator(table));
        createFile(table.getClassName() + "Mapper.xml", rootPath + fileMapper + "\\", sb.toString());
    }

    private static void createMapperFile(Table table) {
        StringBuilder sb = new StringBuilder();
        sb.append("package ").append(DBManager.getConfig().getPackagePreffix()).append(".").append(fileMapper).append(";\n\n");
        sb.append("import java.util.List;\n");
        sb.append("import org.apache.ibatis.annotations.Param;\n");
        sb.append("import ").append(DBManager.getConfig().getPackagePreffix()).append(".").append(fileEntity).append(".").append(table.getClassName()).append(";\n");
        sb.append("import java.util.Map;\n\n");
        sb.append("public interface ").append(table.getClassName()).append("Mapper {\n");
        ReflectUtil.getMapperMethod(sb, MapperGenerator.class, new MapperGenerator(table));
        sb.append("\n}");
        createFile(table.getClassName() + "Mapper.java", rootPath + fileMapper + "\\", sb.toString());
    }

    public static void createJavaFile(Table table) {
        List<JavaGetSet> javaGetSets = new ArrayList<>();
        List<Column> columns = table.getColumns();
        for (Column column : columns) {
            javaGetSets.add(JavaGetSet.getJavaGetSet(column));
        }

        StringBuilder sb = new StringBuilder();
        sb.append("package ").append(DBManager.getConfig().getPackagePreffix()).append(".").append(FileManager.fileEntity).append(";\n\n");
        HashSet<String> setImportStrings = new HashSet<>();
        for (Column column : columns) {
            setImportStrings.add(column.getPropertyType());
        }
        if (isLombok) {
            sb.append("import lombok.Data;\n");
            sb.append("import lombok.ToString;\n");
            sb.append("import lombok.NoArgsConstructor;\n");
            sb.append("import lombok.experimental.Accessors;\n");
        }
        for (String setImportString : setImportStrings) {
            sb.append(StringUtil.getImportString(setImportString)).append("\n");
        }
        sb.append("\n");
        if (DBManager.getConfig().isLombok()) {
            sb.append("@Data\n@ToString\n@Accessors(chain = true)\n@NoArgsConstructor\n");
        }
        sb.append("public class ").append(table.getClassName()).append(" {\n\n");

        for (JavaGetSet javaGetSet : javaGetSets) {
            sb.append(javaGetSet.getPropertyRow()).append("\n");
        }
        sb.setCharAt(sb.length() - 1, ' ');
        if (!isLombok) {
            for (JavaGetSet javaGetSet : javaGetSets) {
                sb.append("\n").append(javaGetSet.getPropertyGet());
                sb.append("\n").append(javaGetSet.getPropertySet());
            }
            sb.append("\n\n\t").append(StringUtil.getToString(table));

        }
        sb.append("\n\n");
        sb.append("}");

        createFile(table.getClassName() + ".java", rootPath + fileEntity + "\\", sb.toString());

    }


    private static void createFile(String fileName, String path, String content) {

        File folder = new File(path);
        if (!folder.exists()) folder.mkdirs();
        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new FileWriter(path + fileName));
            writer.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}
